package service

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"smart-flow/monitor/config"
	"smart-flow/monitor/logger"
	"smart-flow/monitor/proto"
	"time"

	"github.com/shirou/gopsutil/v4/net"
	"go.uber.org/zap"
)

func Exchange(capture *Capture) {
	isp := config.AppConfig().Monitor.Isp
	switch isp {
	case config.IspTelecom:
		doExchange4Ct(capture)
	case config.IspMobile:
		doExchange4Cm()
	}
}

func doExchange4Ct(capture *Capture) {
	var (
		err             error
		currentRxBytes  uint64
		diffBytes       uint64
		downBandwidth   uint64
		downBandwidthV4 uint64
		downBandwidthV6 uint64
	)
	prevRxBytes, err := getRxBytes(config.AppConfig().Monitor.InterfaceName)
	if err != nil {
		logger.Logger.Error("[管控] 初始化流量数据异常", zap.String("interfaceName", config.AppConfig().Monitor.InterfaceName), zap.Error(err))
		os.Exit(1)
	}
	logger.Logger.Info("[管控] 初始化流量数据成功", zap.Uint64("rxBytes", prevRxBytes))
	captureEnable := config.AppConfig().Monitor.Capture
	ticker := time.NewTicker(time.Duration(config.AppConfig().Dy.ServerInterval) * time.Second)
	defer ticker.Stop()
	for range ticker.C {
		currentRxBytes, err = getRxBytes(config.AppConfig().Monitor.InterfaceName)
		if err != nil {
			logger.Logger.Error("[管控] 获取流量数据异常", zap.String("interfaceName", config.AppConfig().Monitor.InterfaceName), zap.Error(err))
			continue
		}
		if currentRxBytes < prevRxBytes {
			diffBytes = 0
		} else {
			diffBytes = currentRxBytes - prevRxBytes
		}
		downBandwidth = diffBytes * config.Byte2Bit / uint64(config.AppConfig().Dy.ServerInterval)
		if captureEnable == 1 {
			downBandwidthV4 = capture.BandwidthV4(uint64(config.AppConfig().Dy.ServerInterval))
			downBandwidthV6 = capture.BandwidthV6(uint64(config.AppConfig().Dy.ServerInterval))
		} else {
			downBandwidthV4 = downBandwidth
			downBandwidthV6 = downBandwidth
		}
		// logger.Logger.Info("[管控] 计算流量数据成功", zap.Uint64("rxBytes", currentRxBytes), zap.Uint64("diffBytes", diffBytes), zap.Int("interval", config.AppConfig().Dy.ServerInterval), zap.Uint64("downBandwidth", downBandwidth))
		if config.AppConfig().Dy.Enable == 1 {
			onlineV4Count, _, expiredV4 := SessionsInstance().GetOnlineV4Stat()
			onlineV6Count, _, expiredV6 := SessionsInstance().GetOnlineV6Stat()
			exchangeReq := &proto.ExchangeReq{
				Timestamp:       time.Now().Unix(),
				MachineId:       config.MachineId,
				MonitorIp:       config.IpAddr,
				Isp:             config.AppConfig().Monitor.Isp,
				InfoUrl:         config.EndpointsInfo.InfoUrl,
				DownUrl:         config.EndpointsInfo.DownUrl,
				ProveV6Url:      config.EndpointsInfo.ProveV6Url,
				DownTotal:       downBandwidth,
				DownTotalV4:     downBandwidthV4,
				DownTotalV6:     downBandwidthV6,
				MaxDownTotal:    config.BandwidthLimitInfo.MaxDownTotal,
				MaxDownTotalV4:  config.BandwidthLimitInfo.MaxDownTotalV4,
				MaxDownTotalV6:  config.BandwidthLimitInfo.MaxDownTotalV6,
				TcpProportion:   config.AppConfig().Dy.TcpProportion,
				UdpProportion:   config.AppConfig().Dy.UdpProportion,
				HttpProportion:  config.AppConfig().Dy.HttpProportion,
				DeltaProportion: config.AppConfig().Dy.DeltaProportion,
				OnlineV4Count:   onlineV4Count,
				OnlineV6Count:   onlineV6Count,
				ExpiredV4:       expiredV4,
				ExpiredV6:       expiredV6,
			}
			err = handleExchangeReq(exchangeReq)
			if err != nil {
				logger.Logger.Error("[管控] 数据上报失败", zap.Error(err))
			}
		}
		prevRxBytes = currentRxBytes
	}
}

func doExchange4Cm() {
	var err error
	ticker := time.NewTicker(time.Duration(config.AppConfig().Dy.ServerInterval) * time.Second)
	defer ticker.Stop()
	for range ticker.C {
		// logger.Logger.Info("[管控] 计算流量数据成功", zap.Uint64("rxBytes", currentRxBytes), zap.Uint64("diffBytes", diffBytes), zap.Int("interval", config.AppConfig().Dy.ServerInterval), zap.Uint64("downBandwidth", downBandwidth))
		if config.AppConfig().Dy.Enable == 1 {
			onlineV4Count, downBandwidthV4, expiredV4 := SessionsInstance().GetOnlineV4Stat()
			onlineV6Count, downBandwidthV6, expiredV6 := SessionsInstance().GetOnlineV6Stat()
			downBandwidthV4 = downBandwidthV4 * config.KBit
			downBandwidthV6 = downBandwidthV6 * config.KBit
			downBandwidth := downBandwidthV4 + downBandwidthV6
			exchangeReq := &proto.ExchangeReq{
				Timestamp:       time.Now().Unix(),
				MachineId:       config.MachineId,
				MonitorIp:       config.IpAddr,
				Isp:             config.AppConfig().Monitor.Isp,
				InfoUrl:         config.PusherEndpointsInfo.InfoUrl,
				DownUrl:         config.PusherEndpointsInfo.DownUrl,
				ProveV6Url:      "",
				DownTotal:       downBandwidth,
				DownTotalV4:     downBandwidthV4,
				DownTotalV6:     downBandwidthV6,
				MaxDownTotal:    config.BandwidthLimitInfo.MaxDownTotal,
				MaxDownTotalV4:  config.BandwidthLimitInfo.MaxDownTotalV4,
				MaxDownTotalV6:  config.BandwidthLimitInfo.MaxDownTotalV6,
				TcpProportion:   config.AppConfig().Dy.TcpProportion,
				UdpProportion:   config.AppConfig().Dy.UdpProportion,
				HttpProportion:  config.AppConfig().Dy.HttpProportion,
				DeltaProportion: config.AppConfig().Dy.DeltaProportion,
				OnlineV4Count:   onlineV4Count,
				OnlineV6Count:   onlineV6Count,
				ExpiredV4:       expiredV4,
				ExpiredV6:       expiredV6,
			}
			err = handleExchangeReq(exchangeReq)
			if err != nil {
				logger.Logger.Error("[管控] 数据上报失败", zap.Error(err))
			}
		}
	}
}

func getRxBytes(interfaceName string) (uint64, error) {
	stats, err := net.IOCounters(true)
	if err != nil {
		return 0, err
	}
	for _, stat := range stats {
		if stat.Name == interfaceName {
			return stat.BytesRecv, nil
		}
	}
	return 0, fmt.Errorf("读取流量数据异常，请检查设备名称或系统权限, 设备名称: %s, 异常信息: %w", interfaceName, err)
}

func handleExchangeReq(exchangeReq *proto.ExchangeReq) error {
	body, _ := json.Marshal(exchangeReq)
	resp, err := http.Post(config.AppConfig().Dy.ServerUrl, "application/json", bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	defer func(resp *http.Response) {
		if resp != nil && resp.Body != nil {
			_ = resp.Body.Close()
		}
	}(resp)
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("http code not ok")
	}
	logger.Logger.Info("[管控] 数据上报成功", zap.Any("data", exchangeReq))
	return nil
}
